﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Diagnostics;
using System.Globalization;
using System.Xml;

namespace FishServe.Core
{
	public class ModuleManifest
	{
		private ModuleManifestType _manifestType;
		private Version _version;
		private DateTime _createdDateTime;
		private string _guid;
		private ManifestModuleInfo _module;
		private List<ManifestModuleInfo> _dependentModules = new List<ManifestModuleInfo>();
		private List<ManifestRegisteredTypeInfo> _registeredTypes = new List<ManifestRegisteredTypeInfo>();
		//private List<ManifestModuleInfo> _childModules = new List<ManifestModuleInfo>();


		public ModuleManifestType ManifestType
		{
			get { return _manifestType; }
		}

		public Version Version
		{
			get { return _version; }
		}

		public DateTime CreatedDateTime
		{
			get { return _createdDateTime; }
		}

		public string Guid
		{
			get { return _guid; }
		}

		public ManifestModuleInfo Module
		{
			get { return _module; }
		}

		public List<ManifestModuleInfo> DependentModules
		{
			get { return _dependentModules; }
		}

		public List<ManifestRegisteredTypeInfo> RegisteredTypes
		{
			get { return _registeredTypes; }
		}

		public List<IncludeFile> IncludeFiles { get; set; }

		//public List<ManifestModuleInfo> ChildModules
		//{
		//    get { return _childModules; }
		//}

		public ModuleManifest(ModuleManifestType manifestType, Version version, DateTime createdDateTime, string guid, ManifestModuleInfo module)
		{
			_manifestType = manifestType;
			_version = version;
			_createdDateTime = createdDateTime;
			_guid = guid;
			_module = module;
		}

		public ModuleManifest(XElement manifestElement)
		{
			XAttribute manifestTypeAttribute = manifestElement.Attribute("ManifestType");
			Trace.Assert(manifestTypeAttribute != null && !String.IsNullOrEmpty(manifestTypeAttribute.Value), "ManifestType attribute missing");
			_manifestType = (ModuleManifestType)Enum.Parse(typeof(ModuleManifestType), manifestTypeAttribute.Value);

			XAttribute versionAttribute = manifestElement.Attribute("Version");
			Trace.Assert(versionAttribute != null && !String.IsNullOrEmpty(versionAttribute.Value), "Manifest Version attribute missing");
			_version = new Version(versionAttribute.Value);

			XAttribute manifestGuidAttribute = manifestElement.Attribute("Guid");
			Trace.Assert(manifestGuidAttribute != null && !String.IsNullOrEmpty(manifestGuidAttribute.Value), "Manifest Guid attribute missing");
			_guid = manifestGuidAttribute.Value;

			XAttribute createdAttribute = manifestElement.Attribute("Created");
			Trace.Assert(createdAttribute != null && !String.IsNullOrEmpty(createdAttribute.Value), "Manifest Created attribute missing");
			_createdDateTime = DateTime.ParseExact(createdAttribute.Value, "yyyy-MM-dd HHHH:mm:ss", CultureInfo.InvariantCulture);

			XElement moduleElement = manifestElement.Element("Module");
			Trace.Assert(moduleElement != null, "Module element missing from Manifest element");
			_module = new ManifestModuleInfo(moduleElement);

			#region RegisteredTypes
			XElement registeredTypesElement = manifestElement.Element("RegisteredTypes");
			if (registeredTypesElement != null)
			{
				foreach (XElement registeredTypeElement in registeredTypesElement.Elements("RegisteredType"))
				{
					XAttribute registeredNameAttribute = registeredTypeElement.Attribute("RegisteredName");

					RegisteredTypeLifetime lifetime = RegisteredTypeLifetime.Undeclared;
					XAttribute lifetimeAttribute = registeredTypeElement.Attribute("Lifetime");
					if (lifetimeAttribute != null && !String.IsNullOrEmpty(lifetimeAttribute.Value))
						lifetime = (RegisteredTypeLifetime)Enum.Parse(typeof(RegisteredTypeLifetime), lifetimeAttribute.Value);

					XElement fromTypeElement = registeredTypeElement.Element("FromType");
					if (fromTypeElement == null)
						throw new Exception(String.Format("Missing FromType element in RegisteredType declaration in ModuleManifest '{0}' v{1}", _module.Name, _module.Version));

					XAttribute fromTypeNameAttribute = fromTypeElement.Attribute("TypeName");
					fromTypeNameAttribute.AssertNotNull(
						"Missing TypeName name attribute in FromType element of RegisteredType declaration in ModuleManifest '{0}' v{1}", 
							_module.Name, _module.Version);
					//if (fromTypeNameAttribute == null)
					//    throw new Exception(String.Format("Missing TypeName name attribute in FromType element of RegisteredType declaration in ModuleManifest '{0}' v{1}", _module.Name, _module.Version));

					ManifestModuleTypeInfo fromModuleInfo = new ManifestModuleTypeInfo(new ManifestModuleInfo(fromTypeElement), fromTypeNameAttribute.Value);

					XElement toTypeElement = registeredTypeElement.Element("ToType");
					if (toTypeElement == null)
						throw new Exception(String.Format("Missing ToType element in RegisteredType declaration in ModuleManifest '{0}' v{1}", _module.Name, _module.Version));

					XAttribute toTypeNameAttribute = toTypeElement.Attribute("TypeName");
					if (toTypeNameAttribute == null)
						throw new Exception(String.Format("Missing TypeName name attribute in ToType element of RegisteredType declaration in ModuleManifest '{0}' v{1}", _module.Name, _module.Version));

					ManifestModuleTypeInfo toModuleInfo = new ManifestModuleTypeInfo(new ManifestModuleInfo(toTypeElement), toTypeNameAttribute.Value);

					ManifestRegisteredTypeInfo registeredTypeInfo = new ManifestRegisteredTypeInfo();
					if (registeredNameAttribute != null)
						registeredTypeInfo.RegisteredName = registeredNameAttribute.Value;

					registeredTypeInfo.Lifetime = lifetime;
					registeredTypeInfo.FromTypeInfo = fromModuleInfo;
					registeredTypeInfo.ToTypeInfo = toModuleInfo;

					_registeredTypes.Add(registeredTypeInfo);

				}
			}
			#endregion
			//Adding DependantModules
			XElement dependentModules = manifestElement.Element("DependentModules");
			if (dependentModules != null)
			{
				_dependentModules = new List<ManifestModuleInfo>();
				foreach (XElement item in dependentModules.Elements("Module"))
				{
					_dependentModules.Add(new ManifestModuleInfo(item));
				}
			}
			this.IncludeFiles = PopulateContentIncludedFiles(manifestElement);
		}

		private List<IncludeFile> PopulateContentIncludedFiles(XElement manifestElement)
		{
			XElement contentElement = manifestElement.Element("Content");
			List<IncludeFile> val = new List<IncludeFile>();

			if (contentElement != null)
			{
				foreach (XElement xIncludedFile in contentElement.Elements("IncludeFile"))
				{
					XAttribute fileHash = xIncludedFile.Attribute("FileHash");
					fileHash.AssertNotNull("Expecting a FileHash attribute in IncludeFile element");

					XAttribute fileName = xIncludedFile.Attribute("Name");
					fileHash.AssertNotNull("Expecting a Name attribute in IncludeFile element");

					IncludeFile incFile = new IncludeFile() { FileHash = fileHash.Value, FileName = fileName.Value };
					val.Add(incFile);
				}
			}
			return val;
		}
	}


	public static class MyIXmlLineInfoExtensions
	{
		/// <summary>
		/// Throws exception if XAttribute is null
		/// </summary>
		/// <param name="xAttribute"></param>
		/// <param name="errorFormatString"></param>
		/// <param name="args"></param>
		public static void AssertNotNull(this XAttribute xAttribute, string errorFormatString, params object[] args)
		{
			if (xAttribute == null)
				throw new Exception(String.Format(errorFormatString, args));
		}	
	}
}
